/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 * Description: UDK atomic operations header file(generic, Architecture-independent)
 * Author: -
 * Create: 2021.4.20
 */

#ifndef UDK_ATOMIC_H
#define UDK_ATOMIC_H

#include <stdint.h>

/* ------------------------- 16 bit atomic operations ------------------------- */

/*
 * Atomic counter define.
 */
typedef struct {
    volatile int16_t cnt;
} udk_atomic16_t;

/*
 * Initialize an atomic counter to 0.
 */
static inline void udk_atomic16_init(udk_atomic16_t *v)
{
    v->cnt = 0;
}

static inline int16_t udk_atomic16_read(const udk_atomic16_t *v)
{
    return v->cnt;
}

static inline void udk_atomic16_set(udk_atomic16_t *v, int16_t new_value)
{
    v->cnt = new_value;
}

/*
 * Add a 16-bit value to an atomic counter and return the before value.
 */
static inline int32_t udk_atomic16_add(udk_atomic16_t *v, int16_t inc)
{
    return __sync_fetch_and_add (&v->cnt, inc);
}

/*
 * Increment a 16-bit atomic counter by 1.
 */
static inline void udk_atomic16_inc(udk_atomic16_t *v)
{
    udk_atomic16_add(v, 1);
}

/*
 * Subtract a 16-bit value from an atomic counter.
 */
static inline void udk_atomic16_sub(udk_atomic16_t *v, int16_t dec)
{
    __sync_fetch_and_sub(&v->cnt, dec);
}

/*
 * Decrement a 16-bit atomic counter by 1.
 */
static inline void udk_atomic16_dec(udk_atomic16_t *v)
{
    udk_atomic16_sub(v, 1);
}

/*
 * udk_atomic16_cmpset, equivalent to(atomic):
 * if (*dst == exp)
 * *dst = src (all 16-bit words)
 */
static inline int udk_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src)
{
    return __sync_bool_compare_and_swap(dst, exp, src);
}

/*
 * Decrement a 16-bit counter by one and return true if the counter is equal to 0.
 */
static inline int udk_atomic16_dec_and_test(udk_atomic16_t *v)
{
    return __sync_sub_and_fetch(&v->cnt, 1) == 0;
}


/* ------------------------- 32 bit atomic operations ------------------------- */

/*
 * Atomic counter define.
 */
typedef struct {
    volatile int32_t cnt;
} udk_atomic32_t;

/*
 * Initialize an atomic counter to 0.
 */
static inline void udk_atomic32_init(udk_atomic32_t *v)
{
    v->cnt = 0;
}

/*
 * Read a 32-bit value from an atomic counter.
 */
static inline int32_t udk_atomic32_read(udk_atomic32_t *v)
{
    return v->cnt;
}

/*
 * Add a 32-bit value to an atomic counter.
 */
static inline void udk_atomic32_add(udk_atomic32_t *v, int32_t inc)
{
    __sync_fetch_and_add(&v->cnt, inc);
}

/*
 * Increment a 32-bit atomic counter by 1.
 */
static inline void udk_atomic32_inc(udk_atomic32_t *v)
{
    udk_atomic32_add(v, 1);
}

/*
 * Subtract a 32-bit value from an atomic counter.
 */
static inline void udk_atomic32_sub(udk_atomic32_t *v, int32_t dec)
{
    __sync_fetch_and_sub(&v->cnt, dec);
}

/*
 * Decrement a 32-bit atomic counter by 1.
 */
static inline void udk_atomic32_dec(udk_atomic32_t *v)
{
    udk_atomic32_sub(v, 1);
}

/*
 * Set an atomic counter to a new 32-bit value.
 */
static inline void udk_atomic32_set(udk_atomic32_t *v, int32_t new_value)
{
    v->cnt = new_value;
}

/*
 * udk_atomic32_cmpset, equivalent to(atomic):
 * if (*dst == exp)
 * *dst = src (all 32-bit words)
 */
static inline int udk_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
{
    return __sync_bool_compare_and_swap(dst, exp, src);
}

/*
 * Add a 32-bit value to an atomic counter and return the result.
 */
static inline int32_t udk_atomic32_add_return(udk_atomic32_t *v, int32_t inc)
{
    return __sync_add_and_fetch(&v->cnt, inc);
}

/*
 * Decrement a 32-bit counter by one and return true if the counter is equal to 0.
 */
static inline int udk_atomic32_dec_and_test(udk_atomic32_t *v)
{
    return __sync_sub_and_fetch(&v->cnt, 1) == 0;
}

/* ------------------------- 64 bit atomic operations ------------------------- */

/*
 * Atomic counter define.
 */
typedef struct {
    volatile int64_t cnt;
} udk_atomic64_t;


/*
 * Initialize an atomic counter to 0.
 */
static inline void udk_atomic64_init(udk_atomic64_t *v)
{
    v->cnt = 0;
}

/*
 * Read a 64-bit value from an atomic counter.
 */
static inline int64_t udk_atomic64_read(udk_atomic64_t *v)
{
    return v->cnt;
}

/*
 * Add a 64-bit value to an atomic counter.
 */
static inline void udk_atomic64_add(udk_atomic64_t *v, int64_t inc)
{
    __sync_fetch_and_add(&v->cnt, inc);
}

/*
 * Increment a 64-bit atomic counter by 1.
 */
static inline void udk_atomic64_inc(udk_atomic64_t *v)
{
    udk_atomic64_add(v, 1);
}

/*
 * udk_atomic64_cmpset, equivalent to(atomic):
 * if (*dst == exp)
 * *dst = src (all 64-bit words)
 */
static inline int udk_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
{
    return __sync_bool_compare_and_swap(dst, exp, src);
}

#endif /* UDK_ATOMIC_H */